package com.cnctema.easecalendar_weekly_view.fragments

import android.content.Context
import android.content.Intent
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.*
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.RelativeLayout
import androidx.fragment.app.Fragment
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import com.cnctema.easecalendar_weekly_view.*
import com.cnctema.easecalendar_weekly_view.activities.AddEventActivity

import com.cnctema.easecalendar_weekly_view.database.*
import com.cnctema.easecalendar_weekly_view.databinding.FragmentWeekBinding
import com.cnctema.easecalendar_weekly_view.extensions.adjustAlpha
import com.cnctema.easecalendar_weekly_view.extensions.config
import com.cnctema.easecalendar_weekly_view.extensions.getContrastColor
import com.cnctema.easecalendar_weekly_view.extensions.plusDayMilli
import com.cnctema.easecalendar_weekly_view.utils.EventWeeklyViewUtils
import com.cnctema.easecalendar_weekly_view.utils.FormatterUtils
import com.cnctema.easecalendar_weekly_view.utils.MyColorUtils
import com.cnctema.easecalendar_weekly_view.views.MyScrollView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.joda.time.DateTime
import org.joda.time.DateTimeZone
import java.util.ArrayList
import java.util.LinkedHashMap

class WeekFragment : Fragment() {
    private lateinit var binding: FragmentWeekBinding
    private val uiScope = CoroutineScope(Dispatchers.Main)

    private lateinit var config : Config                    //默认设置
    private lateinit var dataSource: EventDatabaseDao       //数据库映射

    private var mListener: WeekScrollListener? = null   //滑动监听

    private var dayInCalendar = 7               //当前视图周数
    private var mWeekTimestamp = 0L             //周时间戳
    private var selectedGrid : View? = null     //选中的框
    private var currEventsListHashCode = 0      //当前的日程列表哈希值

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        config = requireContext().config
        dataSource = EventDatabase.getInstance(requireContext()).eventDatabaseDao
        dayInCalendar = config.dayInCalendar
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        // Inflate the layout for this fragment
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_week, container, false)

        mWeekTimestamp = requireArguments().getLong(WEEK_START_TIMESTAMP)

        binding.apply {
            weekEventsScrollview.setOnScrollviewListener(object :
                MyScrollView.ScrollViewListener {
                override fun onScrollChanged(scrollView: MyScrollView, x: Int, y: Int, oldx: Int, oldy: Int) {
                    mListener?.scrollTo(y)
                }
            })
            weekEventsScrollview.viewTreeObserver.addOnGlobalLayoutListener(
                object : ViewTreeObserver.OnGlobalLayoutListener{
                    override fun onGlobalLayout() {
                        updateScrollY(MainActivity.mWeekScrollY)
                        binding.weekEventsScrollview.viewTreeObserver.removeOnGlobalLayoutListener(this)
                    }
                }
            )
            weekEventsGrid.layoutParams.height = resources.getDimension(R.dimen.weekly_view_events_height).toInt()  //设置网格的高度
        }

        hideEventsColumns(dayInCalendar)
        setupDayLabels()      //设置列日期头
        initGrid()          //设置添加事件网格

        return binding.root
    }

    override fun onResume() {
        super.onResume()
        updateEventsView()  //更新事件方格的显示
    }



    //更新事件时间片
    private fun updateEventsView(){
        uiScope.launch {
            val eventsList = getCurrEventsList(dayInCalendar)
            val newHashCode: Int = eventsList.hashCode()
            //判断日历数据是否改变
            if(newHashCode == currEventsListHashCode){
                return@launch
            } else{
                currEventsListHashCode = newHashCode
                val eventViewHashMap = setEventViewHashMap(eventsList)  //按天分类出事件方块哈希表
                clearViewGroup()
                updateEventSquare(eventViewHashMap)
            }
        }
    }

    //数据库操作 - 根据列显示的天数，获取当前事件列表
    private suspend fun getCurrEventsList(dayInCalendar:Int):List<Event>{
        return withContext(Dispatchers.IO){
            val currDay = FormatterUtils.getDateTimeFromTS(mWeekTimestamp)
            dataSource.getEventsByTimeRange(currDay.millis,currDay.millis+DAY_SECONDS*dayInCalendar*1000L-1L)
        }
    }

    // 设置事件矩形的哈希表
    private fun setEventViewHashMap(events: List<Event>): LinkedHashMap<String, ArrayList<EventWeeklyView>> {
        val eventViewHashMap = LinkedHashMap<String, ArrayList<EventWeeklyView>>()  //事件方块HashMap

        val edgeStDateTime = FormatterUtils.getDateTimeFromTS(mWeekTimestamp)   //当前边界开始时间DateTime
        val edgeStMilli = edgeStDateTime.millis                             //当前开始时间边界
        val edgeEndMilli = edgeStDateTime.plusDays(dayInCalendar).millis    //当前结束时间边界

        for (event in events) {
            val evStMilli = if(event.startTimeMilli>=edgeStMilli) event.startTimeMilli else edgeStMilli //有效绘制时间起点
            val evEdMilli = if(event.endTimeMilli<=edgeEndMilli) event.endTimeMilli else edgeEndMilli   //有效绘制时间结尾

            val stDateTime = DateTime(evStMilli, DateTimeZone.getDefault()) //事件开始时间
            val edDateTime = DateTime(evEdMilli, DateTimeZone.getDefault()) //事件结束时间
            val crossDay = edDateTime.dayOfYear - stDateTime.dayOfYear  //将事件拆分为跨天数子事件，跨天数子事件占用绘制天数
            val crossEdgeDay = stDateTime.dayOfYear-edgeStDateTime.dayOfYear    //距离边界开始时间的天数(可作为列标志)

            for(i in 0..crossDay) { //对于每一个事件拆分后的跨天数子事件，进行列分段
                //计算当前子事件开始时间
                val currViewStMilli =
                    if(i == 0)
                        evStMilli
                    else
                        edgeStMilli.plusDayMilli(crossEdgeDay+i) //距离边界开始时间的毫秒数

                val sDateTime = DateTime(currViewStMilli, DateTimeZone.getDefault())    //子事件开始时间的DateTime
                val dayCode = sDateTime.toString(FormatterUtils.DAYCODE_PATTERN)   //计算键
                if (!eventViewHashMap.containsKey(dayCode)) {   //若哈希表里不存在键，创建一个
                    eventViewHashMap[dayCode] = ArrayList()
                }

                //计算当前子事件结束时间
                val currViewEdMilli =
                    if(crossDay == 0 || i==crossDay)
                        evEdMilli
                    else
                        edgeStMilli.plusDayMilli(i+crossEdgeDay+1)-1  //距离边界结束时间的毫秒数

                //设置EventWeeklyView
                val eventWeekly = EventWeeklyView(id = event.eventId,
                    title = event.eventTitle, location = event.eventLocation,
                    type = event.eventType,
                    stTime = currViewStMilli,edTime = currViewEdMilli
                ) //设置事件方块属性
                eventViewHashMap[dayCode]?.add(eventWeekly)
            }
        }
        return eventViewHashMap
    }



    //更新事件方块
    private fun updateEventSquare(eventViewHashMap:LinkedHashMap<String, ArrayList<EventWeeklyView>>){
        for(colIndex in 0 until dayInCalendar){ //取出每一天的事件方块列表
            val currDateTime = FormatterUtils.getDateTimeFromTS(mWeekTimestamp+colIndex* DAY_SECONDS)   //当前时间DateTime
            val dayCode = currDateTime.toString(FormatterUtils.DAYCODE_PATTERN)  //key值

            //传入当前天数的事件方块列表，使用周视图事件方块排布算法计算
            val evList = EventWeeklyViewUtils.eventViewArrangement(eventViewHashMap[dayCode])?:continue // 计算事件方块排布信息
            val mLayout = getColumnWithId(colIndex)     //获取当前放置的列 ViewGroup

            for (evView in evList){
                drawEventSquare(evView,mLayout){
                    Intent(context, AddEventActivity::class.java).apply { //点击修改事件
                        putExtra(EVENT_ID,evView.id)
                        startActivity(this)
                    }
                }
            }
        }
    }

    //绘制事件方块
    private fun drawEventSquare(evView:EventWeeklyView,mLayout:ViewGroup,myOnClick:(evView:View)->Unit){
        val miniHeight = resources.getDimension(R.dimen.weekly_view_minimal_event_height).toInt()   //事件方块最小高度
        val oneMinHeight = resources.getDimension(R.dimen.weekly_view_one_minute_height)    //事件一分钟对应的方块高度
        val density = Math.round(resources.displayMetrics.density)   //获取屏幕密度

        val stDateTime = DateTime(evView.stTime, DateTimeZone.getDefault())     //开始时间 DateTime
        val edDateTime = DateTime(evView.edTime, DateTimeZone.getDefault())     //结束时间 DateTime
        val stMinutes = stDateTime.minuteOfDay  //事件开始时的分钟数
        val stMinuteHeight = stMinutes * oneMinHeight      //事件方块开始高度
        val durationHeight = ((edDateTime.minuteOfDay - stMinutes)*oneMinHeight).toInt()   //持续时间

        val colIndex = evView.colIndex     //事件方块列标号
        val colNum = evView.colNum          //事件方块宽度分子
        val colDenom = evView.colDenom         //事件方块宽度分母

        (LayoutInflater.from(context)
            .inflate(R.layout.item_week_event_square,null,false) as TextView).apply {
            var mBackgroundColor = setEvColorByType(context,evView.type)    //TODO: 设置成多组自定义颜色
            var mTextColor = mBackgroundColor.getContrastColor() //根据背景确定对比色字体
            if(evView.edTime < System.currentTimeMillis()){ //此事件已过当前时间
                mBackgroundColor = mBackgroundColor.adjustAlpha(LOW_ALPHA)  //设置透明度
                mTextColor = mTextColor.adjustAlpha(Hight_ALPHA)
            }

            background = ColorDrawable(mBackgroundColor)
            setTextColor(mTextColor)
            text = evView.title+"\n\n"+evView.location

            mLayout.addView(this)    //添加事件方块
            y = stMinuteHeight+density
            //设置矩形框View属性
            (layoutParams as RelativeLayout.LayoutParams).apply {
                width = mLayout.width - 1
                width /= colDenom  // 根据重叠个数设置宽度
                if(colDenom>1){
                    x = width * colIndex.toFloat() //根据重叠次序设置位置
                    width *= colNum
                    if(colIndex != 0){
                        x += density    //左移，左边位移留空
                        if(colIndex+1 != colDenom){   //事件不是最后一个或第一个
                            width -=density //减少宽度
                        }
                    }
                    width -=density     //减少宽度
                }

                minHeight = if(durationHeight>miniHeight) (durationHeight-density) else miniHeight
                maxHeight = minHeight
            }

            // 设置点击监听
            setOnClickListener{
                myOnClick(it)
            }
        }

    }

    //根据事件type类型赋值颜色
    private fun setEvColorByType(context: Context, type:Int):Int{
        return ContextCompat.getColor(context,
            MyColorUtils.eventViewColorList[type% MyColorUtils.eventViewColorList.size]
        )
    }



    // 设置周数文字框
    private fun setupDayLabels(){
        var curDay = FormatterUtils.getDateTimeFromTS(mWeekTimestamp)
        for(i in 1..dayInCalendar){
            val view = layoutInflater.inflate(R.layout.item_weekly_view_dayletter_textview, null, false) as TextView
            val dayLetter = FormatterUtils.getDayLetter(requireContext(), curDay.dayOfWeek)
            view.apply {
                text = "${dayLetter}\n${curDay.dayOfMonth}"
                if(curDay.dayOfYear == DateTime(System.currentTimeMillis()).dayOfYear){
                    this.setTextColor(ContextCompat.getColor(context,R.color.colorPrimary))
                }
            }
            binding.weekDaylettersHolder.addView(view,
                LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    1f
                ))
            curDay = curDay.plusDays(1)
        }
    }

    //设置网格
    private fun initGrid(){
        (0 until dayInCalendar).map { getColumnWithId(it) }
            .forEachIndexed{ index, viewGroup ->
                val gestureDetector = getViewGestureDetector(viewGroup, index)
                viewGroup.setOnTouchListener{view,motionEvent->
                    gestureDetector.onTouchEvent(motionEvent)
                    true
                }
            }
    }

    //清除周视图每列的事件方块
    private fun clearViewGroup(){
        (0 until dayInCalendar).map { getColumnWithId(it) }
            .forEachIndexed{ index, viewGroup ->
                viewGroup.removeAllViews()  //清除所有列viewGroup
            }

    }

    //获得网格手势操作
    private fun getViewGestureDetector(view: ViewGroup, index: Int): GestureDetector {
        return GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
            override fun onSingleTapUp(e: MotionEvent): Boolean {
                selectedGrid?.animation?.cancel()
                selectedGrid?.visibility = View.GONE

                val rowHeight = resources.getDimension(R.dimen.weekly_view_row_height)
                val hour = (e.y/rowHeight).toInt()

                //选择的网格方块
                selectedGrid = (LayoutInflater.from(context)
                    .inflate(R.layout.item_week_grid,null,false) as ImageView).apply {
                    view.addView(this)
                    layoutParams.width = view.width //宽
                    layoutParams.height = rowHeight.toInt() //高
                    y = hour * rowHeight

                    //设置点击后的动作
                    setOnClickListener{
                        val timestamp = mWeekTimestamp + index * 24*60*60 + hour*60*60
                        Intent(context, AddEventActivity::class.java).apply {
                            putExtra(WEEK_START_TIMESTAMP, timestamp)
                            startActivity(this)
                        }
                    }
                    //设置动画
                    animate().setStartDelay(500L).alpha(0f).withEndAction{
                        visibility = View.GONE
                    }
                }
                return super.onSingleTapUp(e)
            }
        })
    }

    //隐藏多余的网格
    private fun hideEventsColumns(startHideIndex:Int){
        if (startHideIndex>6){return}
        for(i in startHideIndex..6){
            when(i){
                0->binding.weekColumn0.visibility = View.GONE
                1->binding.weekColumn1.visibility = View.GONE
                2->binding.weekColumn2.visibility = View.GONE
                3->binding.weekColumn3.visibility = View.GONE
                4->binding.weekColumn4.visibility = View.GONE
                5->binding.weekColumn5.visibility = View.GONE
                6->binding.weekColumn6.visibility = View.GONE
            }
        }
    }



    //根据id获得周放置框
    private fun getColumnWithId(id:Int):ViewGroup =
        when(id){
            1->binding.weekColumn1
            2->binding.weekColumn2
            3->binding.weekColumn3
            4->binding.weekColumn4
            5->binding.weekColumn5
            6->binding.weekColumn6
            else->binding.weekColumn0
        }


    //滑动监听类
    interface WeekScrollListener{
        fun scrollTo(y:Int)
    }

    //设置监听
    fun setListener(listener: WeekScrollListener) {
        mListener = listener
    }

    //页面上下滑动
    fun updateScrollY(y: Int) {
        binding.weekEventsScrollview.scrollY = y
    }

}
